﻿#include "NotificationIcon.hpp"
#include "../../frontend/cat/Resource.h"
#include "../../libcat/include/util/base.hpp"
#include "../../libcat/include/cat.hpp"
#include <tchar.h>
#include "../../libcat/include/header/cat-config.hpp"

namespace {

	NotificationIcon* s_instance = NULL;

	HINSTANCE g_hInst = NULL;

	UINT const WMAPP_NOTIFYCALLBACK = WM_APP + 3;

	wchar_t const szWindowClass[] = L"NotificationIcon";

	const int CATUID = 0x3333;

	static bool flage = false;

	DWORD WINAPI UI_Start(LPVOID lpParam)
	{
		ShellExecute(NULL, 0, L"UI.exe", 0, 0, SW_SHOWNORMAL);
		return 0;
	}

	BOOL AddNotificationIcon(HWND hwnd)
	{
		NOTIFYICONDATA nid = { sizeof(nid) };
		nid.hWnd = hwnd;
		nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
		nid.uID= CATUID;
		nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
		nid.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_NOTIFICATIONICON));
		LoadString(g_hInst, IDS_TOOLTIP, nid.szTip, ARRAYSIZE(nid.szTip));
		Shell_NotifyIcon(NIM_ADD, &nid);

		// NOTIFYICON_VERSION_4 is prefered
		nid.uVersion = NOTIFYICON_VERSION_4;
		return Shell_NotifyIcon(NIM_SETVERSION, &nid);
	}

	BOOL DeleteNotificationIcon(HWND hwnd)
	{
		NOTIFYICONDATA nid = { sizeof(nid) };
		nid.hWnd = hwnd;
		nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
		nid.uID = CATUID;
		nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
		nid.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_NOTIFICATIONICON));
		LoadString(g_hInst, IDS_TOOLTIP, nid.szTip, ARRAYSIZE(nid.szTip));
		return Shell_NotifyIcon(NIM_DELETE, &nid);
	}

	BOOL ShowPrintJobBalloon(HWND hwnd)
	{
		// Display a balloon message for a print job with a custom icon
		NOTIFYICONDATA nid = { sizeof(nid) };
		nid.uFlags = NIF_INFO;
		nid.hWnd = hwnd;
		nid.uID = CATUID;
		nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON;
		LoadString(g_hInst, IDS_PRINTJOB_TITLE, nid.szInfoTitle, ARRAYSIZE(nid.szInfoTitle));
		LoadString(g_hInst, IDS_PRINTJOB_TEXT, nid.szInfo, ARRAYSIZE(nid.szInfo));
		nid.hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_NOTIFICATIONICON));
		return Shell_NotifyIcon(NIM_MODIFY, &nid);
	}

	void RegisterWindowClass(PCWSTR pszClassName, PCWSTR pszMenuName, WNDPROC lpfnWndProc, HINSTANCE hInst)
	{
		WNDCLASSEX wcex = { sizeof(wcex) };
		wcex.style = CS_HREDRAW | CS_VREDRAW;
		wcex.lpfnWndProc = lpfnWndProc;
		wcex.hInstance = hInst;
		wcex.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_NOTIFICATIONICON));
		wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
		wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
		wcex.lpszMenuName = pszMenuName;
		wcex.lpszClassName = pszClassName;
		RegisterClassEx(&wcex);
	}
	
	void ShowContextMenu(HWND hwnd, POINT pt)
	{
		HMENU hMenu = LoadMenu(g_hInst, MAKEINTRESOURCE(IDC_CONTEXTMENU));
		if (hMenu)
		{
			HMENU hSubMenu = GetSubMenu(hMenu, 0);
			if (hSubMenu)
			{
				// our window must be foreground before calling TrackPopupMenu or the menu will not disappear when the user clicks away
				SetForegroundWindow(hwnd);

				// respect menu drop alignment
				UINT uFlags = TPM_RIGHTBUTTON;
				if (GetSystemMetrics(SM_MENUDROPALIGNMENT) != 0)
				{
					uFlags |= TPM_RIGHTALIGN;
				}
				else
				{
					uFlags |= TPM_LEFTALIGN;
				}

				TrackPopupMenuEx(hSubMenu, uFlags, pt.x, pt.y, hwnd, NULL);
			}
			DestroyMenu(hMenu);
		}
	}

	void ChangeMode(int i) {
		int modeid = cat_get_curent_mode_id();
		cat_release_mode(modeid);
		cat_init_mode(i);
		cat_set_curent_mode_id(i);
	}

	LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		static HWND s_hwndFlyout = NULL;
		static BOOL s_fCanShowFlyout = TRUE;

		switch (message)
		{
		case WM_CREATE:
			// add the notification icon
			if (!AddNotificationIcon(hwnd))
			{
				MessageBox(hwnd,
					L"Please read the ReadMe.txt file for troubleshooting",
					L"Error adding icon", MB_OK);
			}
			ShowPrintJobBalloon(hwnd);
			break;
		case WM_COMMAND:
		{
			int const wmId = LOWORD(wParam);
			// Parse the menu selections:
			switch (wmId)
			{
			case IDM_SHOWCAT:
				cat_set_gui_visible(true);
				break;
			case IDM_HIDECAT:
				cat_set_gui_visible(false);
				break;
			case IDM_MODE1:
				ChangeMode(1);
				break;
			case IDM_MODE2:
				ChangeMode(2);
				break;
			case IDM_MODE3:
				ChangeMode(3);
				break;
			case IDM_UI:
				NotificationIcon::GetInstance()->OnStartUi();
				break;
			case IDM_EXIT:
				DestroyWindow(hwnd);
				break;
			default:
				return DefWindowProc(hwnd, message, wParam, lParam);
			}
		}
		case WMAPP_NOTIFYCALLBACK: {
			switch (LOWORD(lParam))
			{
				case NIN_SELECT:
					blog(LOG_DEBUG, "NIN_SELECT");
					cat_set_gui_visible(true);
					break;
				case NIN_BALLOONTIMEOUT:
					blog(LOG_DEBUG, "NIN_BALLOONTIMEOUT");
					break;

				case NIN_BALLOONUSERCLICK:
					blog(LOG_DEBUG, "NIN_BALLOONUSERCLICK");
					NotificationIcon::GetInstance()->OnStartUi();
					break;
				case WM_CONTEXTMENU:
					blog(LOG_DEBUG, "WM_CONTEXTMENU");
					POINT const pt = { LOWORD(wParam), HIWORD(wParam) };
					ShowContextMenu(hwnd, pt);
					break;
			}
			break;
		}
		case WM_DESTROY:
			cat_close_gui();
			DeleteNotificationIcon(hwnd);
			PostMessage(hwnd,0,0,0);
			break;
		}
		return DefWindowProc(hwnd, message, wParam, lParam);
	}
}

NotificationIcon* NotificationIcon::GetInstance()
{
	if (s_instance == NULL) {
		s_instance = new NotificationIcon();
	}
	return s_instance;
}

void NotificationIcon::ReleaseInstance()
{
	if (s_instance != NULL) {
		delete s_instance;
	}

	s_instance = NULL;
}
void NotificationIcon::Start()
{
	th = new std::thread(&NotificationIcon::Run, this);
}

void NotificationIcon::Stop()
{
	PostMessage(m_hWnd, WM_DESTROY, 0, 0);
	th->join();
	th = nullptr;
}

void NotificationIcon::OnStartUi()
{
	WCHAR text[32] = { 0 };
	HWND uiHwnd = FindWindow(NULL,L"Bongo Cat 设置");
	//当识别到已经打开UI窗口时。将UI窗口移到顶层
	if (uiHwnd!=NULL)
		SetWindowPos(uiHwnd, HWND_TOP, 1, 1, 1, 1, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
	//未识别到UI窗口时启动生成UI窗口的线程
	else
		CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UI_Start, cat_get_handle(), 0, NULL);
}

NotificationIcon::NotificationIcon():
	m_hWnd(NULL)
	,th(NULL)
{

}


NotificationIcon::~NotificationIcon()
{

}

void NotificationIcon::Run()
{
	g_hInst = GetModuleHandle(NULL);

	RegisterWindowClass(szWindowClass, MAKEINTRESOURCE(IDI_NOTIFICATIONICON), WndProc, g_hInst);

	WCHAR szTitle[100];
	LoadString(g_hInst, IDS_APP_TITLE, szTitle, ARRAYSIZE(szTitle));
	m_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, 250, 200, NULL, NULL, g_hInst, NULL);
	if (m_hWnd)
	{
		::ShowWindow(m_hWnd, SW_HIDE);

		// Main message loop:
		MSG msg;
		while (GetMessage(&msg, m_hWnd, 0, 0))
		{
			if (msg.message == 0)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	::DestroyWindow(m_hWnd); 
	
	return;
}
